﻿using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics;
using System.Reflection;
using конфигуратор;

namespace Wpfконфигуратор.Визуализаторы
{
    public class ДинамикОбъект   :INotifyPropertyChanged
    {
        private Паспорт _паспорт;
        Паспорт Паспорт
        {
            get { return _паспорт; }
            set
            {  
                if ( value != null )
                {
                    _паспорт = value;
                    _паспорт.PropertyChanged += ПаспортPropertyChanged;
                }
            }
        }
        public object Объект{
                                        get { return _паспорт.Объект; } 
                                        set {  _паспорт.Объект = value;}
                                        }  
        public Type Тип { 
                                    get { return  _паспорт.Тип ; }
                                    set {  _паспорт.Тип = value; }
                                }
        public ДинамикОбъект (  )  {Паспорт= new Паспорт();}
        public ДинамикОбъект ( Паспорт паспорт)
        {
            Паспорт = паспорт;
            ЗаполнитьЧлены(); 
        }

        public ДинамикОбъект ( object объект )
        {
            Паспорт = new Паспорт();
            Объект = объект;
        }

        public ДинамикОбъект ( Type тип )
        {
            Паспорт = new Паспорт();
            Тип = тип;
        }

        // если в паспорте изменен тип или объект перестроить редактор под новый тип
         void ПаспортPropertyChanged ( object sender, PropertyChangedEventArgs e )
        {
             if (e.PropertyName=="Тип")
            ЗаполнитьЧлены();
             if (e.PropertyName=="Объект")
                 Обновить("все");
        }

        void ЗаполнитьЧлены()
        {
            _описаниеТипа = null;
            _поляОбъекта = null;
            _поляТипа = null;
            _конструкторы = null;
            _свойстваОбъекта = null;
            _свойстваТипа = null;
            _методыОбъекта = null;
            _методыТипа = null;
            _событияОбъекта = null;
            _событияТипа = null;
            _элементы = null;
                //Элементы = ЭлементМассива.ДатьЭлементыМассива( this );
                //Элементы = ДинСписок.ДатьСписок( this );

                Обновить("все");
        }

        public event PropertyChangedEventHandler PropertyChanged;
        public void Обновить (string чтоМенять)
        {
            if ( PropertyChanged != null && чтоМенять == "все" )
            {
                PropertyChanged( this, new PropertyChangedEventArgs( "" ) );
                return;
            }

            if ( чтоМенять != "Свойства" && _поляОбъекта != null )
            {
                foreach ( var поле in ПоляОбъекта )
                {
                    поле.Обновить();
                }
            }
            if ( СвойстваОбъекта != null )
            {
                foreach ( var свойство in СвойстваОбъекта )
                {
                    свойство.Обновить();
                }
            }
            //_элементы = ЭлементМассива.ДатьЭлементыМассива( this );
        }

        private ОписаниеТипа _описаниеТипа;
        public ОписаниеТипа ОписаниеТипа { get { return _описаниеТипа ?? (_описаниеТипа = new ОписаниеТипа(Тип)); } }

        public object АтрибутыПользовательские { get { return Тип.GetCustomAttributes(false); } }

        public bool ЭтоСписок { get { return Объект is IList; } }

        public Type ТипЭлемента 
        {
            get
            {
                if ( Тип.IsGenericType )
                {
                    return Тип.GetGenericArguments()[0];
                }
                if ( Тип.IsArray )
                {
                    return Тип.GetElementType();
                }
                return null;
            }
        }

        private object _элементы;
        public object Элементы { get { return _элементы ?? (_элементы = ЭлементМассива.ДатьЭлементыМассива(this)); } }

        private КонструкторВизуализатор[] _конструкторы;
        public КонструкторВизуализатор[] Конструкторы { get { return _конструкторы ?? (_конструкторы= КонструкторВизуализатор.ДатьСписокКонструкторов( this )); } }

        private Событие[] _событияОбъекта;
        public Событие[] СобытияОбъекта { get { return _событияОбъекта ?? (_событияОбъекта = Событие.ДатьСписокСобытийОбъекта(this)); } }
        private Событие[] _событияТипа;
        public Событие[] СобытияТипа { get { return _событияТипа ?? (_событияТипа = Событие.ДатьСписокСобытийТипа( this )); } }

        private List<ПолеВизуализатор> _поляОбъекта;
        public List<ПолеВизуализатор> ПоляОбъекта { get
        {
            Debug.WriteLine("Поля объекта =" + _описаниеТипа.Имя);
            if ( _поляОбъекта == null ) 
            _поляОбъекта = ПолеВизуализатор.ДатьСписокПолейОбъекта(this);
            return _поляОбъекта;

            return _поляОбъекта ?? (_поляОбъекта = ПолеВизуализатор.ДатьСписокПолейОбъекта( this ));
        } }     // Поля!=null? Поля.Where(поле => !поле.F.IsStatic).ToList():null;
        
        private List<ПолеВизуализатор> _поляТипа;
        public List<ПолеВизуализатор> ПоляТипа { get { return _поляТипа?? (_поляТипа=ПолеВизуализатор.ДатьСписокПолейТипа(this)); } }

        private СвойствоОболочка[] _свойстваОбъекта;
        public СвойствоОболочка[] СвойстваОбъекта { get { return _свойстваОбъекта??(_свойстваОбъекта=СвойствоОболочка.ДатьСписокСвойствОбъекта(this)); } }
        private СвойствоОболочка[] _свойстваТипа;
        public СвойствоОболочка[] СвойстваТипа { get { return _свойстваТипа ?? (_свойстваТипа = СвойствоОболочка.ДатьСписокСвойствТипа( this )); } }

        private Метод[] _методыОбъекта;
        public Метод[] МетодыОбъекта { get { return _методыОбъекта??(_методыОбъекта=Метод.ДатьСписокМетодовОбъекта(this)); } }
        private Метод[] _методыТипа;
        public Метод[] МетодыТипа { get { return _методыТипа??(_методыТипа=Метод.ДатьСписокМетодовТипа(this)); } }

    
        public static bool ЯвляетсяФлаговымПеречислителем (Type тип)
        {
             return тип.IsDefined(typeof (FlagsAttribute), false);
        }



        public object ВызватьКонструктор ( params  object[] значенияПараметров )
        {
            if ( Тип == null ) return null;
            Type[] типыПараметров;
            if ( значенияПараметров == null )
            {
                типыПараметров = Type.EmptyTypes;
            }
            else
            {
                //var типы = значенияПараметров.Select( з => з.GetType()).ToArray();
                типыПараметров = new Type[значенияПараметров.Length];
                var i = 0;
                foreach ( var o in значенияПараметров )
                {
                    типыПараметров[i] = o.GetType();
                    i++;
                }
            }

            ConstructorInfo ctor = this.Тип.GetConstructor( типыПараметров );
            if ( ctor == null )
                return null;
            return ctor.Invoke( значенияПараметров );
        }
        
        public object ПолучитьСвойство ( string имяСвойства )
        {
            if ( Тип == null ) return null;
            return Тип.InvokeMember( имяСвойства, BindingFlags.GetProperty, null /* Binder */,Объект, null /* args */);
        }
        
        public static object ПолучитьСвойствоОбъекта (object объект,  string имяСвойства )
        {
            if ( объект == null ) return null;
            try
            {
                  return объект.GetType().InvokeMember( имяСвойства, BindingFlags.GetProperty, null /* Binder */, объект, null /* args */);
            }
            catch (Exception)
            {

                return null;
            }
           
        }

        public object УстановитьСвойство ( string имяСвойства, object значение )
        {
            if ( Тип == null ) return null;
            try
            {
                return Тип.InvokeMember( имяСвойства, BindingFlags.SetProperty, null /* Binder */, Объект, new object[] { значение } );
            }
            catch
            {
                return null;
            }

        }
        public object ПолучитьПоле ( string имяПоля )
        {
            if ( Тип == null ) return null;
            return Тип.InvokeMember( имяПоля, BindingFlags.GetField, null /* Binder */, Объект, null /* args */);
        }
        public static object ПолучитьПолеОбъекта (object объект, string имяПоля )
        {
            if ( объект == null ) return null;
            try
            {
                  return объект.GetType().InvokeMember( имяПоля, BindingFlags.GetField, null /* Binder */, объект, null /* args */);
            }
            catch (Exception)
            {

                return null;
            }
            
        }

        public object УстановитьПоле ( string имяПоля, object значениеПоля )
        {
            if ( Тип == null ) return null;
            try
            {
                return Тип.InvokeMember( имяПоля, BindingFlags.SetField, null /* Binder */, Объект ,new object[] { значениеПоля } );
            }
            catch
            {
                return null;
            }
            
        }
      

        public object ВызватьМетод ( string имяМетода, Type[] типыПараметров, object[] значениеПараметров )
        {
            if ( типыПараметров.Length != значениеПараметров.Length )
                throw new Exception( "разная длина параметров" );
            var метод = Тип.GetMethod( имяМетода, типыПараметров );
            if ( метод == null ) throw new Exception( "нет такого метода" );
            return метод.Invoke( Объект, BindingFlags.InvokeMethod, null, значениеПараметров, null );
        }
        public object ВызватьМетод ( string имяМетода, params object[] значениеПараметров )
        {
            try
            {
                return Тип.InvokeMember( имяМетода, BindingFlags.InvokeMethod, null, Объект, значениеПараметров );
            }
            catch ( Exception )
            {
                return null;
            }

        }
    }
}
